Github
PoststypescriptDeclaration Merging

Declaration Merging

A set of two-state buttons that can be toggled on or off

Declaration Merging

선언 병합이란 컴파일러가 같은 이름으로 선언된 두개를 하나의 정의로 합치는 것을 말한다.

선언 타입네임스페이스타입
네임스페이스XX
클래스XX
열거형XX
인터페이스X
타입 aliasX
함수X
변수X

Interface merge

  • 두 선언의 멤버들을 같은 이름의 단일 인터페이스로 결합한다.
  • 같은 이름의 멤버가 다른 타입을 가지고 있으면 error가 발생한다.
  • 이후에 선언된 인터페이스가 높은 우선순위를 가진다.
1interface Cloner { 2 clone(animal: Animal): Animal; 3} 4interface Cloner { 5 clone(animal: Sheep): Sheep; 6} 7interface Cloner { 8 clone(animal: Dog): Dog; 9 clone(animal: Cat): Cat; 10}
1interface Cloner { 2 clone(animal: Dog): Dog; 3 clone(animal: Cat): Cat; 4 clone(animal: Sheep): Sheep; 5 clone(animal: Animal): Animal; 6}

namespace merge

  • 네임스페이스를 병합하기 위해서, 각 네임스페이스에 선언된 export 된 인터페이스로부터 타입 정의가 병합되며, 내부에 병합된 인터페이스 정의가 있는 단일 네임스페이스가 형성된다.
  • 네임스페이스가 네임스페이스와 값 둘 다 만든다.
1namespace Animals { 2 export class Zebra {} 3} 4namespace Animals { 5 export interface Legged { 6 numberOfLegs: number; 7 } 8 export class Dog {} 9}
1namespace Animals { 2 export interface Legged { 3 numberOfLegs: number; 4 } 5 export class Zebra {} 6 export class Dog {} 7}

export하지 않는 멤버를 내부에서 사용하는 경우 주의점

1namespace Animal { 2 let haveMuscles = true; 3 export function animalsHaveMuscles() { 4 return haveMuscles; 5 } 6} 7namespace Animal { 8 export function doAnimalsHaveMuscles() { 9 return haveMuscles; // 오류, haveMuscles가 여기에 접근할 수 없기 때문에 10 } 11}

Class, Function, enum과 namespace의 merge

namespace는 다른 타입의 유형과 병합이 가능할 정도로 유연하다.

패턴과 같다. typescript는 선언 병합을 통해서 타입을 안전하게 보존하며 정의할 수 있다.

merging namespace with class

1class Album { 2 label: Album.AlbumLabel; 3} 4namespace Album { 5 export class AlbumLabel {} 6}

다른 클래스 내에서 관리되는 클래스. namespace를 사용해서 기존 클래스에 정적 멤버를 추가할 수 있다.

merging namespace with function

1function buildLabel(name: string): string { 2 return buildLabel.prefix + name + buildLabel.suffix; 3} 4namespace buildLabel { 5 export let suffix = ""; 6 export let prefix = "Hello, "; 7} 8console.log(buildLabel("Sam Smith"));

merging namespace with enum

1enum Color { 2 red = 1, 3 green = 2, 4 blue = 4, 5} 6namespace Color { 7 export function mixColor(colorName: string) { 8 if (colorName == "yellow") { 9 return Color.red + Color.green; 10 } else if (colorName == "white") { 11 return Color.red + Color.green + Color.blue; 12 } else if (colorName == "magenta") { 13 return Color.red + Color.blue; 14 } else if (colorName == "cyan") { 15 return Color.green + Color.blue; 16 } 17 } 18}

모듈 보강

1// observable.ts 2export class Observable<T> { 3 // ... 연습을 위해 남겨둠 ... 4} 5// map.ts 6import { Observable } from "./observable"; 7 8declare module "./observable" { 9 interface Observable<T> { 10 map<U>(f: (x: T) => U): Observable<U>; 11 } 12} 13 14Observable.prototype.map = function (f) { 15 // ... 연습을 위해 남겨둠 16}; 17 18// consumer.ts 19import { Observable } from "./observable"; 20import "./map"; 21 22let o: Observable<number>; 23o.map((x) => x.toFixed());

위 상황은 Observable을 import해서 prototype에 map을 추가했을 때 consumer에서 type을 알 수 없다.

따라서 Observable을 merge를 통해서 map을 보강해준다.

전역 보강

1// observable.ts 2export class Observable<T> { 3 // ... 연습을 위해 남겨둠 ... 4} 5declare global { 6 interface Array<T> { 7 toObservable(): Observable<T>; 8 } 9} 10Array.prototype.toObservable = function () { 11 // ... 12};

모듈 내부에서 전역 스코프에 선언을 추가해줄 수 있다.

disallowed merges

클래스는 다른 클래스나 변수와 병합할 수 없다.